



function setPixel(imageData, x, y, r, g, b, a) {
    index = (x + y * imageData.width) * 4;
    imageData.data[index+0] = r;
    imageData.data[index+1] = g;
    imageData.data[index+2] = b;
    imageData.data[index+3] = a;
}

var createCanvas = function (width, height){
    var buffer = document.createElement('canvas');
    buffer.width = width;
    buffer.height = height;
    return buffer;
};


var renderToCanvas = function (canvas, renderFunction) {
    var ctx = canvas.getContext('2d');
    ctx.clearRect ( 0 , 0 , canvas.width , canvas.height );
    renderFunction(ctx);
    return canvas;
};




function _metaball (_x, _y, _r, _behaviour) {
    this._x = (_x) ? _x : 0.0;
    this._y = (_y) ? _y : 0.0;
    this._r = (_r) ? _r : 0.0;
    this._r2 = (this._r * this._r);
    this._r2_100 = (this._r * this._r) * 1000;
    
    this._behaviour = (_behaviour) ? _behaviour : null;
    
    this.Equation = function(x,y){
    
        var dist2 = (x-this._x)*(x-this._x) + (y-this._y)*(y-this._y) ;
        
        if( dist2 < this._r2_100 )
        {
            //var dist = Math.sqrt( dist2 );
            
            return this._r2 / dist2;
                
            
            //return (this._r / dist );
        }
        else
            return 0;
    };
};
function metaball (_x, _y, _r, _behaviour){
    return new _metaball( _x, _y, _r, _behaviour );
};


var _metaworld = cc.Node.extend({
    _w:1,
    _h:1,
    
    _metaballs:[],
    _canvas:null,
    _IsoImage:null,
    
    // constante
    MIN_THRESHOLD:0.95,
    MAX_THRESHOLD:1.05,
    
    ctor:function (_W, _H) {
        this._super();
        this.scheduleUpdate();
        
        this._w = _W;
        this._h = _H;
    
        
        var __metaballs = this._metaballs;
        this.marching10 = marchingSquare(this._w,
                                        this._h,
                                        10, 
                                        this.MIN_THRESHOLD,
                                        this.MAX_THRESHOLD,
                                        function(X,Y){
                                            var sum = 0.0;
                                            for(var k=0;k<__metaballs.length;k++)
                                            { 
                                                sum += __metaballs[k].Equation(X,Y); 
                                            }
                                            return sum;
                                        });
                                        
        this.marching5 = marchingSquare(this._w,
                                        this._h,
                                        25, 
                                        this.MIN_THRESHOLD,
                                        this.MAX_THRESHOLD,
                                        function(X,Y){
                                            var sum = 0.0;
                                            for(var k=0;k<__metaballs.length;k++)
                                            { 
                                                sum += __metaballs[k].Equation(X,Y); 
                                            }
                                            return sum;
                                        });
                                        
                                        
        this.marching2 = marchingSquare(this._w,
                                        this._h,
                                        2, 
                                        this.MIN_THRESHOLD,
                                        this.MAX_THRESHOLD,
                                        function(X,Y){
                                            var sum = 0.0;
                                            for(var k=0;k<__metaballs.length;k++)
                                            { 
                                                sum += __metaballs[k].Equation(X,Y); 
                                            }
                                            return sum;
                                        });
                                        
    },
    
    
    handleTouch:function(touch){
    },
    handleTouchMove:function(touchLocation){
    },
    
    
    addMetaball:function( _metaball ){
        this._metaballs.push( _metaball );
    },
    
    update:function(dt){
        
        for(var k=0;k<this._metaballs.length;k++)
        { 
            if( this._metaballs[k]._behaviour != null )
            {
                this._metaballs[k]._behaviour(dt);
            }
        }
        
    },
    
    clearIsoImage:function(){
        
        for(var x=0;x<this._w;x++)
        {
            for(var y=0;y<this._h;y++)
            {
                setPixel(this._IsoImage, x, y, 0, 0, 0, 255);
            }
        }
    },
    
    draw:function (cocos_ctx) {
        
        if(this._canvas == null)
            this._canvas = createCanvas(this._w, this._h);
        
        var __this = this;
        
        
        var cached = renderToCanvas( this._canvas, function (ctx) {
            __this.internalDraw(ctx);
        });
        cocos_ctx.drawImage(this._canvas, 0, -this._h);
    },
    
    
    drawTest:[ "BASE", "Circle","Circle_Optim","GRAD","META_GRAD","META_GRAD_BORDER","MARCHING_10","MARCHING_5","MARCHING_2"],
    drawActualTest:0,
    
    internalDraw:function (ctx) {
        
        var renderMode = this.drawTest[ this.drawActualTest ];
        
        
        
        if( renderMode == "ISO" )
        {
            if(this._IsoImage == null)
                this._IsoImage = ctx.createImageData(this._w, this._h);

            this.clearIsoImage();
            this.drawISO(ctx);
            ctx.putImageData(this._IsoImage, 0, 0);
        }
        else if( renderMode == "MARCHING_10" )
        {
            this.marching10.resolve();
            this.marching10.draw(ctx);
        }
        else if( renderMode == "MARCHING_5" )
        {
            this.marching5.resolve();
            this.marching5.draw(ctx);
        }
        else if( renderMode == "MARCHING_2" )
        {
            this.marching2.resolve();
            this.marching2.draw(ctx);
        }
        else if( renderMode == "GRAD" )
        {
            this.drawGradient(ctx);
        }
        else if( renderMode == "META_GRAD" )
        {
            this.drawMetaGradient(ctx);
        }
        else if( renderMode == "META_GRAD_BORDER" )
        {
            this.drawMetaGradientBorder(ctx);
        }
        
        else if( renderMode == "ISO_optim" )
        {
            if(this._IsoImage == null)
                this._IsoImage = ctx.createImageData(this._w, this._h);

            this.clearIsoImage();
            this.drawISO_Optim(ctx);
            ctx.putImageData(this._IsoImage, 0, 0);
        }
        else if( renderMode == "Circle" )
        {
            this.drawCircle(ctx);
        }
        else if( renderMode == "Circle_Optim" )
        {
            this.drawCircle_Optim(ctx);
        }
        else if( renderMode == "BASE" )
        {
            this.drawNormalBall(ctx);
        }
    },
    
    drawNormalBall:function(ctx){
        
        for(var k=0;k<this._metaballs.length;k++)
        { 
            cc.drawingUtil.drawCircle(cc.p(this._metaballs[k]._x,
                                            this._metaballs[k]._y), 
                                    this._metaballs[k]._r
                                    , 0, 20, false);
        }
    },
    
    drawGradient:function(ctx){
        
        
            
        for(var a=0;a<this._metaballs.length;a++)
        {
            var X = this._metaballs[a]._x;
            var Y = this._metaballs[a]._y;
            var R = this._metaballs[a]._r /** 2*/;
            
            
            var grad = ctx.createRadialGradient(X, this._h-Y, R, X, this._h-Y, 3*R);
            grad.addColorStop(0, 'rgba(' + 61 +',' + 135 + ',' + 255 + ',255)');
            grad.addColorStop(1, 'rgba(' + 61 +',' + 135 + ',' + 255 + ',0)');
        
        
            ctx.beginPath();
            ctx.fillStyle = grad;
            ctx.arc(X, this._h-Y, 3*R, 0, Math.PI*2);
            ctx.closePath();
            ctx.fill();
            
        }
        
        
    },
    
    drawMetaGradient:function(ctx){
        this.drawGradient(ctx);
        
        // metaballize
        
        var imageData = ctx.getImageData(0,0,this._w,this._h);
        var pix = imageData.data;
        for (var i = 0, n = pix.length; i <n; i += 4) {
            if(pix[i+3]< 230 ){
                pix[i+3]=0;
            }
            else {
                pix[i+3]=255;
            }
        }
        ctx.putImageData(imageData, 0, 0);    
    },
    
    drawMetaGradientBorder:function(ctx){
        
        this.drawGradient(ctx);
        // metaballize
        
        var imageData = ctx.getImageData(0,0,this._w,this._h);
        var pix = imageData.data;
        for (var i = 0, n = pix.length; i <n; i += 4) {
            if(pix[i+3]< 200 ){
                pix[i+3]=0;
            }
            else if(pix[i+3]< 250 ){
                pix[i+0]=44;
                pix[i+1]=107;
                pix[i+2]=215;
                pix[i+3]=255;
            }
            else {
                pix[i+3]=255;
            }
        }
        ctx.putImageData(imageData, 0, 0);    
    },
    
    drawISO:function( ctx ){
        for(var x=0;x<this._w;x++)
        {
            for(var y=0;y<this._h;y++)
            {
                var sum = 0.0;
                for(var k=0;k<this._metaballs.length;k++)
                { 
                    sum += this._metaballs[k].Equation(x,y); 
                }
            
                if( sum <= this.MAX_THRESHOLD && sum >= this.MIN_THRESHOLD )
                {
                    setPixel(this._IsoImage, x, this._h-y, 255, 0, 0, 255);
                    //cc.drawingUtil.drawCircle(cc.p(x,y),1,0, 6, false);
                }
            } 
        }
    },
    
    drawISO_Optim:function(ctx){
        
        
        
        for(var a=0;a<this._metaballs.length;a++)
        {
            var X = this._metaballs[a]._x - this._metaballs[a]._r*4;
            if(X<0) X=0;
            
            var Y = this._metaballs[a]._y - this._metaballs[a]._r*4;
            if(Y<0) Y=0;
            
            var Loop = this._metaballs[a]._r*8;
            
            for(var x=X; (x<this._w) && (x<(X+Loop));x++)
            {
                for(var y=Y; (y<this._h) && (y<(Y+Loop));y++)
                {
                    var sum = 0.0;

                    for(var k=0;k<this._metaballs.length;k++)
                    { 
                        sum += this._metaballs[k].Equation(x,y); 
                    }

                    if( sum <= this.MAX_THRESHOLD && sum >= this.MIN_THRESHOLD )
                    {
                        setPixel(this._IsoImage, x, this._h-y, 255, 0, 0, 255);
                        //cc.drawingUtil.drawCircle(cc.p(x,y),1,0, 6, false);
                    }
                } 
            }
        }
    },
    
    
    drawCircle:function( ctx ){
        for(var x=0;x<this._w;x++)
        {
            for(var y=0;y<this._h;y++)
            {
                var sum = 0.0;
                for(var k=0;k<this._metaballs.length;k++)
                { 
                    sum += this._metaballs[k].Equation(x,y); 
                }
            
                if( sum <= this.MAX_THRESHOLD && sum >= this.MIN_THRESHOLD )
                {
                    cc.drawingUtil.drawCircle(cc.p(x,y),1,0, 6, false);
                }
            } 
        }
    },
    
    drawCircle_Optim:function(ctx){
        
        for(var a=0;a<this._metaballs.length;a++)
        {
            var X = this._metaballs[a]._x - this._metaballs[a]._r*4;
            if(X<0) X=0;
            
            var Y = this._metaballs[a]._y - this._metaballs[a]._r*4;
            if(Y<0) Y=0;
            
            var Loop = this._metaballs[a]._r*8;
            
            for(var x=X; (x<this._w) && (x<(X+Loop));x++)
            {
                for(var y=Y; (y<this._h) && (y<(Y+Loop));y++)
                {
                    var sum = 0.0;

                    for(var k=0;k<this._metaballs.length;k++)
                    { 
                        sum += this._metaballs[k].Equation(x,y); 
                    }

                    if( sum <= this.MAX_THRESHOLD && sum >= this.MIN_THRESHOLD )
                    {
                        cc.drawingUtil.drawCircle(cc.p(x,y),1,0, 6, false);
                    }
                } 
            }
        }
    },
    

    
});
function metaworld (_W, _H){
    return new _metaworld(_W, _H);
};



var _metaScene = cc.Layer.extend({
   
   MouseX:0,
   MouseY:0,
   obj:null,
   
    init:function () {
        var selfPointer = this;
        //////////////////////////////
        // 1. super init first
        this._super();

        this.setTouchEnabled(true);
        this.setKeyboardEnabled(true);
        
        
        var size = cc.Director.getInstance().getWinSize();

        // background

        var background = cc.Sprite.create("res/LightedBackground.png");
        background.setPosition(cc.p(size.width / 2, size.height / 2));
        background.setVisible(true);
        background.setAnchorPoint(cc.p(0.5, 0.5));
        this.addChild(background, 0);

    
        //Menu (back button)
        var items = new Array();
        var p = cc.p( (-size.width/2) + 20 ,
                      (-size.height/2) + 30 );
        items[0] = createButton("Back");
        items[0].setPosition( p );
        items[0].setCallback(this,
            function () {
                myApp.GoToScene( new HelloWorldScene() );
            });
        var myMenu = cc.MenuCustom.create(items);
        this.addChild(myMenu);
        
        this.scheduleUpdate();
        
        
        
        
        //specific
        
        
        // test meta
        var Coef = 1;
        
        var world = metaworld(size.width/Coef,size.height/Coef);
        world.setScale(Coef);
        this.addChild(world);
        
        // center ball
        world.addMetaball( metaball (size.width/2,size.height/2, 20) );
        
        // user ball
        this.obj = { x:10, y:10};
        var obj = this.obj;
        world.addMetaball( metaball (0, 0, 10, function(dt){
            this._x = obj.x;
            this._y = obj.y;
        }) );
        
        
        
        // cpu ball
        world.addMetaball( metaball (0, 0, 10, function(dt){
            this._x += 10*dt;
            this._y += 20*dt;
        }) );
        
        
        
        
        
        //world (world button)
        
        var LabelTTF = cc.LabelTTF.create("Test 1", "Arial", 17);
        LabelTTF.setAnchorPoint(cc.p(0, 0.5));
        LabelTTF.setPosition(cc.p(500,30));
        this.addChild(LabelTTF);
        LabelTTF.setString( world.drawTest[ world.drawActualTest ] );
        
        
        var Number = cc.LabelTTF.create("Number", "Arial", 17);
        Number.setAnchorPoint(cc.p(0, 0.5));
        Number.setPosition(cc.p(500,100));
        this.addChild(Number);
        Number.setString( world._metaballs.length );
        
        
        items = new Array();
        // switch render
        p = cc.p( 0 , (-size.height/2) + 30 );
        items[0] = createButton("Switch");
        items[0].setPosition( p );
        items[0].setCallback(this,
            function () {
                world.drawActualTest++;
                world.drawActualTest = world.drawActualTest % world.drawTest.length;
                
                LabelTTF.setString( world.drawTest[ world.drawActualTest ] );
            });
            
        // push/pop
        p = cc.p( 0 , (-size.height/2) + 80 );
        items[1] = createButton("Push");
        items[1].setPosition( p );
        items[1].setCallback(this,
            function () {
                
                world.addMetaball( metaball ( cc.RANDOM_0_1()*500, cc.RANDOM_0_1()*400, cc.RANDOM_0_1()*20) );
                Number.setString( world._metaballs.length );
            });
            
        p = cc.p( 0 , (-size.height/2) + 130 );
        items[2] = createButton("Pop");
        items[2].setPosition( p );
        items[2].setCallback(this,
            function () {
                
                world._metaballs.pop();
                Number.setString( world._metaballs.length );
            });
            
        myMenu = cc.MenuCustom.create(items);
        this.addChild(myMenu);
        
        
        return true;
    },
    
    update:function (dt) {
    },
    
    // inputs 
    onTouchesBegan:function (pTouch, event) {
    },
    onTouchesMoved:function(pTouch,pEvent){
        if(pTouch[0])
        {
            var localTouch = this.convertTouchToNodeSpace(pTouch[0]);
            var worldTouch = this.convertToWorldSpace(localTouch);
            worldTouch = cc.Director.getInstance().convertToGL(worldTouch);
            this.MouseX = worldTouch.x;
            this.MouseY = worldTouch.y;
            
            this.obj.x = this.MouseX ;
            this.obj.y = this.MouseY ;
        }
    },
    onTouchesEnded:function (pTouch,pEvent){
    },
    onKeyUp:function(e){
    },
    onKeyDown:function(e){
    },
});


var metaScene = cc.Scene.extend({
    onEnter:function () {
        this._super();
        var layer = new _metaScene();
        layer.init();
        this.addChild(layer);
    }
});


